/*
 * SMA.java
 *
 * Created on June 17, 2002, 11:56 AM
 */

package redman;

import java.lang.*;
import org.apache.log4j.*;

/**
 *
 * @author  llaranje
 * @version 
 */
public class SMA {
    String appname = null;
    String state = null;
    String newstate = null;
    String hamodel = null;
    String remotestate = "UNKNOWN";
    boolean designation = false;
    boolean failoverenabled = true;
    boolean intransition = false;
    
    private static Category trace = Category.getInstance(SMA.class);
    
    /** Creates new SMA */
    public SMA(String appname, String hamodel, String initstate, 
                               boolean designation, boolean failoverenabled) {
        this.appname = appname;
        this.state = initstate;
        this.hamodel = hamodel;
        this.designation = designation;
        this.failoverenabled = failoverenabled;
    }

    public String getState() {
        synchronized (state) {
            return(state);
        }
    }
    
    public void setDesignation(boolean val) {
        designation = val;
    }
    
    public boolean getDesignation() {
        return(designation);
    }
    
    public String getCurrentState() {
        synchronized (state) {
            if (intransition) {
                return("IN TRANSITION from state " + state + " to state " + newstate);
            }
            else return(state);
        }
    }
    
    public String getTargetState() {
        synchronized (state) {
            if (intransition) {
                return(newstate);
            }
            else return(state); 
        }
    }
    
    public synchronized String setFailoverEnabled(boolean value) {
        boolean oldvalue = failoverenabled;
        failoverenabled = value;
        if ((oldvalue == false) && (value == true) && state.equals("STANDBY") 
                                                   && !remotestate.equals("ACTIVE")) {
            return(run("APP ACTIVE"));
        }
        else return(null);
    }
    
    public synchronized boolean getFailoverEnabled() {
        return(failoverenabled);
    }
        
    public synchronized boolean intransition() {
        if (intransition) return(true);
        else return(false);
    }
    
    public String getNewState() {
        if (intransition) {
            synchronized (state) {
                return(newstate);
            }
        }
        else return(null);
    }
    
    public String getRemoteState() {
        synchronized (remotestate) {
            return(remotestate);
        }
    }
    
    public void setRemoteState(String stateval) {
        synchronized (remotestate) {
            remotestate = stateval;
        }
    }
    
    private String exec10(String event) {
        String retval = null;
                
        if (event.equals("RHOST DOWN")) {
            remotestate = "OFFLINE";
            if (state.equals("OFFLINE") && failoverenabled) newstate = "ACTIVE";
        }
        
        if (event.equals("RAPP FAILED")) {
            remotestate = "FAILED";
            if (state.equals("OFFLINE") && failoverenabled) newstate = "ACTIVE";
        }
        
        if (event.equals("SMA INITIALIZE")) {
            if (remotestate.equals("UNKNOWN")) {
                if (designation) newstate = "ACTIVE";
                else newstate = "OFFLINE";
            }
            else if (remotestate.equals("OFFLINE"))  newstate = "ACTIVE";
            else if (remotestate.equals("ACTIVE")) newstate = "OFFLINE";
            else if (remotestate.equals("FAILED"))  newstate = "ACTIVE";
        }
        
        if (event.equals("RAPP OFFLINE")) {
            remotestate = "OFFLINE";
            if (state.equals("OFFLINE") && failoverenabled) newstate = "ACTIVE";
        }
        
        if (event.equals("RAPP ACTIVE")) remotestate = "ACTIVE";
        
        if (event.equals("APP FAILED")) newstate = "FAILED";
                
        synchronized (state) {
            if ((newstate != null) && (!state.equals(newstate))) {
                trace.debug("APP will change state from " + state + " to " + newstate);
                state = newstate;
                retval = newstate;
            }
        }
        return(retval);
    }    
    
    private String exec11(String event) {
        String retval = null;
        
        if (event.equals("COMMIT")) {
            if (intransition) {
                trace.info(appname + " committed new state: " + newstate);
                synchronized (state) {
                    state = newstate;
                    newstate = null;
                    intransition = false;
                    return(state);
                }
            }
            else return(null);
        }
        else {
            if (intransition) {
                if (event.equals("APP FAILED")) {
                    synchronized (state) {
                        state = "FAILED";
                        newstate = "FAILED";
                        intransition = false;
                        return("FAILED");
                    }
                }
                else if (event.equals("APP OFFLINE")) {
                    synchronized (state) {
                        state = "OFFLINE";
                        newstate = "OFFLINE";
                        intransition = false;
                        return("OFFLINE");
                    }
                }
                else return("IN TRANSITION from state " + state + " to state " + newstate );
            }
        }
        
        if (event.equals("APP STARTED")) {
            if (!state.equals("INIT")) newstate = "INIT";
        }
        
        // ATTENTION: FORCEFUL EVENTS - CHECK MUST HAVE BEEN DONE OUTSIDE OF SMA
        if (event.equals("APP ACTIVE")) {
            if (state.equals("STANDBY")) newstate = "ACTIVE";   // FORCEFUL 
        }
        if (event.equals("APP STANDBY")) {
            if (state.equals("ACTIVE")) newstate = "STANDBY";   // FORCEFUL
        }
        
        if (event.equals("APP INITIALIZED")) {
            if (state.equals("INIT")) {
                if (failoverenabled) {
                    if (hamodel.equals("1")) newstate = "ACTIVE";
                    else {
                        if (remotestate.equals("INIT")) {
                            if (designation) newstate = "ACTIVE";
                            else newstate = "STANDBY";
                        }
                        else if (remotestate.equals("UNKNOWN"))  newstate = "ACTIVE";
                        else if (remotestate.equals("STANDBY"))   newstate = "ACTIVE";
                        else if (remotestate.equals("ACTIVE"))  newstate = "STANDBY";
                        else if (remotestate.equals("FAILED"))   newstate = "ACTIVE";
                        else if (remotestate.equals("OFFLINE"))  newstate = "ACTIVE";
                        else if (remotestate.equals("MAINTENANCE"))  newstate = "ACTIVE";
                    }
                }
                else newstate = "MAINTENANCE";
            }
            else  {
                newstate = state;
                intransition = true;
                return(state);
            }
        }
        
        if (event.equals("APP FAILED")) newstate = "FAILED";
        
        if (event.equals("APP OFFLINE") && (state != "OFFLINE")) newstate = "OFFLINE";
        
        if (event.equals("APP TEST")) {
            if ((hamodel.equals("1+1") && state.equals("STANDBY")) ||
                                    (hamodel.equals("1") && state.equals("ACTIVE"))) {
                newstate = "MAINTENANCE";
                failoverenabled = false;
            }
        }
        
        if (event.equals("APP UNTEST")) {
            if (state.equals("MAINTENANCE")) {
                if (hamodel.equals("1+1")) {
                    if (!remotestate.equals("ACTIVE")) newstate = "ACTIVE";
                    else newstate = "STANDBY";
                }
                else if (hamodel.equals("1")) newstate = "ACTIVE";
                failoverenabled = true;
            }
        }
                
        if (event.equals("RHOST DOWN")) {
            if (!remotestate.equals("OFFLINE")) remotestate = "FAILED";
            if (state.equals("STANDBY") && failoverenabled) newstate = "ACTIVE";
        }
        
        if (event.equals("RAPP FAILED")) {
            if (!remotestate.equals("OFFLINE")) remotestate = "FAILED";
            if (state.equals("STANDBY") && failoverenabled) newstate = "ACTIVE";
        }
        
        if (event.equals("RAPP OFFLINE")) {
            remotestate = "OFFLINE";
            if (state.equals("STANDBY") && failoverenabled) newstate = "ACTIVE";
        }
                
        if (event.equals("RAPP STANDBY")) {
            remotestate = "STANDBY";
            if (state.equals("STANDBY") && designation) newstate = "ACTIVE";  // Split brain recovery (step 2)
        }
        
        if (event.equals("RAPP ACTIVE")) {
            remotestate = "ACTIVE";
            if (state.equals("ACTIVE")) newstate = "STANDBY";  // Split brain recovery (step 1)
        }
        
        if (event.equals("RAPP MAINTENANCE")) {
            remotestate = "MAINTENANCE";
            if (state.equals("STANDBY") && failoverenabled) newstate = "ACTIVE";
        }
        
        if (event.equals("RAPP INIT")) {
            remotestate = "INIT";
            if (state.equals("STANDBY") && failoverenabled) newstate = "ACTIVE";
        }        
            
        trace.debug("Checking new state: " + newstate + ". Will synchronize on state");
        synchronized (state) {
            if ((newstate != null) && !state.equals(newstate)) {
                if (newstate.equals("INIT") || newstate.equals("FAILED")) {
                    trace.info(appname + " changed state from " + state + " to " + newstate);
                }
                else if (!newstate.equals("OFFLINE")) {
                    trace.info("RedMan will command " + appname 
                                        + " to change state from " + state + " to " + newstate);
                }
                if (newstate.equals("FAILED") || newstate.equals("OFFLINE")) {
                    state = newstate;
                    retval = state;
                    newstate = null;
                }
                else {
                    intransition = true;
                    retval = newstate;
                }
            }
        }
        return(retval);
    }
    
    synchronized String run(String event) {
        String retval = null;

        if (hamodel.equals("1+1") || hamodel.equals("1")) {
            trace.debug("RUN (before execution): Event " + event + ", state = " + state 
                                             + ", newstate = " + newstate
                                             + ", remotestate = " + remotestate 
                                             + ", hamodel = " + hamodel);
            
            retval = exec11(event);
            
            trace.debug("RUN (after execution) : Processed event " + event + ", state = " + state 
                                             + ", newstate = " + newstate
                                             + ", remotestate = " + remotestate 
                                             + ", hamodel = " + hamodel);
        }
        if (hamodel.equals("1+0")) {
            trace.debug("RUN (before execution): Event " + event + ", state = " + state 
                                             + ", remotestate = " + remotestate 
                                             + ", hamodel = " + hamodel);
            retval = exec10(event);
            
            trace.debug("RUN (after execution): Processed event " + event + ", state = " + state 
                                             + ", remotestate = " + remotestate 
                                             + ", hamodel = " + hamodel);
        }
        return(retval);
    }
}